package fw.qaz.asmx.reader.parser;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.DisplayMetrics;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Vector;

import fw.qaz.asmx.reader.utils.Convertor;
import fw.qaz.asmx.reader.utils.DetectCharset;
import fw.qaz.asmx.reader.view.ReadView;

//感谢 Kiritor 提供的解析并显示TXT的思路
public class TxtParser extends AbsEbookParser
{
    private File book;
    private MappedByteBuffer mbb;
    private int m_mbBufLen = 0;
    private int m_mbBufBegin = 50; //50
    private int m_mbBufEnd = 0;
    private String charset = "GBK";
    private int mWidth;
    private int mHeight;

    private Vector<String> m_lines = new Vector<>();

    private int txtSize = 40;
    private int txtColor = Color.BLACK;
    private int paddingLR = 15; // 左右与边缘的距离
    private int paddingTB = 25; // 上下与边缘的距离

    private int lineCount; // 每页可以显示的行数
    private float mVisibleHeight; // 绘制内容的宽
    private float mVisibleWidth; // 绘制内容的宽
    private boolean m_isfirstPage;
    private boolean m_islastPage;
    private int btmTxtSize = 18;//底部文字大小
    private int spaceSize = 20;//行间距大小
    private int curProgress = 0;//当前的进度
    private String fileName = "";

    private Paint txtPaint;
    private Paint btmPaint;//底部文字绘制

    private Bitmap curPageBitmap;
    private Canvas curPageCanvas;

    private ReadView rv;

    public TxtParser(ReadView rv, int txtSp, int fg, int bg)
    {
        DisplayMetrics metric = new DisplayMetrics();
        ((Activity) rv.getContext()).getWindowManager().getDefaultDisplay().getMetrics(metric);

        this.rv = rv;
        mWidth = metric.widthPixels;
        mHeight = metric.heightPixels;
        txtSize = Convertor.px2sp(rv.getContext(), txtSp);
        txtColor = fg;

        curPageBitmap =
                Bitmap.createBitmap(metric.widthPixels, 5,
                                    Bitmap.Config.ARGB_8888);//当前页位图
        curPageCanvas = new Canvas(curPageBitmap);//显示当前页位图
        rv.setBitmaps(curPageBitmap);
        //

        txtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        txtPaint.setTextAlign(Paint.Align.LEFT);
        txtPaint.setTextSize(txtSize);

        //
        rv.setTextSize(txtSize);
        rv.setTextColor(txtColor);
        rv.setLineSpacing(spaceSize, 0.86F);
        rv.setBackgroundColor(bg);
        rv.setPadding(paddingLR, paddingTB, paddingLR, paddingTB);

        mVisibleWidth = mWidth - paddingLR * 2;
        mVisibleHeight = mHeight - paddingTB * 2;
        int totalSize = txtSize + spaceSize;
        lineCount = (int) ((mVisibleHeight - 20) / totalSize); // 可显示的行数
        //底部文字绘制
        btmPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        btmPaint.setTextAlign(Paint.Align.LEFT);
        btmPaint.setTextSize(btmTxtSize);
        btmPaint.setColor(txtColor);
        //
    }

    @Override public void openBook(String strFilePath)
    {
        try
        {
            book = new File(strFilePath);
            this.fileName = book.getName().substring(0, book.getName().indexOf("."));
            long lLen = book.length();
            m_mbBufLen = (int) lLen;
            mbb = new RandomAccessFile(book, "r")
                    .getChannel()
                    .map(FileChannel.MapMode.READ_ONLY, 0, lLen);
            Charset cs = DetectCharset.detect(book);
            if (cs != null)
            {
                this.charset = cs.name();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    protected byte[] readParagraphBack(int nFromPos)
    {
        int i;
        byte b0, b1;
        switch (charset)
        {
        case "UTF-16LE":
            i = nFromPos - 2;
            while (i > 0)
            {
                b0 = mbb.get(i);
                b1 = mbb.get(i + 1);
                if (b0 == 0x0a && b1 == 0x00 && i != nFromPos - 2)
                {
                    i += 2;
                    break;
                }
                i--;
            }
            break;
        case "UTF-16BE":
            i = nFromPos - 2;
            while (i > 0)
            {
                b0 = mbb.get(i);
                b1 = mbb.get(i + 1);
                if (b0 == 0x00 && b1 == 0x0a && i != nFromPos - 2)
                {
                    i += 2;
                    break;
                }
                i--;
            }
            break;
        default:
            i = nFromPos - 1;
            while (i > 0)
            {
                b0 = mbb.get(i);
                if (b0 == 0x0a && i != nFromPos - 1)
                {
                    i++;
                    break;
                }
                i--;
            }
            break;
        }
        if (i < 0)
        {
            i = 0;
        }
        int nParaSize = nFromPos - i;
        int j;
        byte[] buf = new byte[nParaSize];
        for (j = 0; j < nParaSize; j++)
        {
            buf[j] = mbb.get(i + j);
        }
        return buf;
    }

    protected byte[] readParagraphForward(int nFromPos)
    {
        int i = nFromPos;
        byte b0, b1;
        // 根据编码格式判断换行
        switch (charset)
        {
        case "UTF-16LE":
            while (i < m_mbBufLen - 1)
            {
                b0 = mbb.get(i++);
                b1 = mbb.get(i++);
                if (b0 == 0x0a && b1 == 0x00)
                {
                    break;
                }
            }
            break;
        case "UTF-16BE":
            while (i < m_mbBufLen - 1)
            {
                b0 = mbb.get(i++);
                b1 = mbb.get(i++);
                if (b0 == 0x00 && b1 == 0x0a)
                {
                    break;
                }
            }
            break;
        default:
            while (i < m_mbBufLen)
            {
                b0 = mbb.get(i++);
                if (b0 == 0x0a)
                {
                    break;
                }
            }
            break;
        }
        int nParaSize = i - nFromPos;
        byte[] buf = new byte[nParaSize];
        for (i = 0; i < nParaSize; i++)
        {
            buf[i] = mbb.get(nFromPos + i);
        }
        return buf;
    }

    protected Vector<String> pageDown()
    {
        String strParagraph = "";
        Vector<String> lines = new Vector<>();
        while (lines.size() < lineCount && m_mbBufEnd < m_mbBufLen)
        {
            byte[] paraBuf = readParagraphForward(m_mbBufEnd); // 读取一个段落
            m_mbBufEnd += paraBuf.length;
            try
            {
                strParagraph = new String(paraBuf, charset);
            }
            catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
            String strReturn = "\r";//May be Mac edited text,Who know~~
            if (strParagraph.contains("\r\n"))
            {
                strReturn = "\r\n";
                strParagraph = strParagraph.replaceAll("\r\n", "");
            }
            else if (strParagraph.contains("\n"))
            {
                strReturn = "\n";
                strParagraph = strParagraph.replaceAll("\n", "");
            }

            if (strParagraph.length() == 0)
            {
                lines.add(strParagraph);
            }
            while (strParagraph.length() > 0)
            {
                int nSize = txtPaint.breakText(strParagraph, true, mVisibleWidth, null);
                lines.add(strParagraph.substring(0, nSize));
                strParagraph = strParagraph.substring(nSize);
                if (lines.size() >= lineCount)
                {
                    break;
                }
            }
            if (strParagraph.length() != 0)
            {
                try
                {
                    m_mbBufEnd -= (strParagraph + strReturn).getBytes(charset).length;
                }
                catch (UnsupportedEncodingException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return lines;
    }

    protected void pageUp()
    {
        if (m_mbBufBegin < 0)
        {
            m_mbBufBegin = 0;
        }
        Vector<String> lines = new Vector<>();
        String strParagraph = "";
        while (lines.size() < lineCount && m_mbBufBegin > 0)
        {
            Vector<String> paraLines = new Vector<>();
            byte[] paraBuf = readParagraphBack(m_mbBufBegin);
            m_mbBufBegin -= paraBuf.length;
            try
            {
                strParagraph = new String(paraBuf, charset);
            }
            catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
            strParagraph = strParagraph.replaceAll("\r\n", "");
            strParagraph = strParagraph.replaceAll("\n", "");

            while (strParagraph.length() > 0)
            {
                int nSize = txtPaint.breakText(strParagraph, true, mVisibleWidth, null);
                paraLines.add(strParagraph.substring(0, nSize));
                strParagraph = strParagraph.substring(nSize);
            }
            lines.addAll(0, paraLines);
        }
        while (lines.size() > lineCount)
        {
            try
            {
                m_mbBufBegin += lines.get(0).getBytes(charset).length;
                lines.remove(0);
            }
            catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
        }
        m_mbBufEnd = m_mbBufBegin;
    }

    @Override public void prevPage()
    {
        if (m_mbBufBegin <= 0)
        {
            m_mbBufBegin = 0;
            m_isfirstPage = true;
            return;
        }
        else
        {
            m_isfirstPage = false;
        }
        m_lines.clear();
        pageUp();
        m_lines = pageDown();
    }

    @Override public void nextPage()
    {
        if (m_mbBufEnd >= m_mbBufLen)
        {
            m_islastPage = true;
            return;
        }
        else
        {
            m_islastPage = false;
        }
        m_lines.clear();
        m_mbBufBegin = m_mbBufEnd;
        m_lines = pageDown();
    }

    @Override public void freshUI()
    {
        rv.setText("");
        if (m_lines.size() == 0)
        {
            m_lines = pageDown();
        }
        //
        if (m_lines.size() > 0)
        {
            StringBuilder sb = new StringBuilder();
            for (String strLine : m_lines)
            {
                sb.append(strLine).append("\n");
            }
            rv.setText(sb.toString());
        }

        //Calc percent
        float fPercent = (float) (m_mbBufBegin * 1.0 / m_mbBufLen);
        String strPercent = df.format(fPercent * 100) + "%";

        //Fast clear bottom
        btmPaint.setXfermode(pdfClr);
        curPageCanvas.drawRect(0, 0, mWidth, mHeight, btmPaint);
        btmPaint.setXfermode(pdfSrc);

        //Calc and draw bottom words
        curProgress = (int) round1(fPercent * 100, 0);
        int nPercentWidth = (int) btmPaint.measureText("99.9%") + 1;
        curPageCanvas.drawText(strPercent, mWidth - nPercentWidth, 0, btmPaint);

        curDate.setTime(System.currentTimeMillis());
        String str = sdf.format(curDate);
        curPageCanvas.drawText(str, 5, 0, btmPaint);
        int titleWidth = (int) btmPaint.measureText("《" + fileName + "》") + 1;
        curPageCanvas
                .drawText("《" + fileName + "》", (mWidth - titleWidth) / 2, 0, btmPaint);
    }

    private PorterDuffXfermode pdfClr = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
    private PorterDuffXfermode pdfSrc = new PorterDuffXfermode(PorterDuff.Mode.SRC);

    private DecimalFormat df = new DecimalFormat("#0.0");
    private Date curDate = new Date();
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());

    private static double round1(double v, int scale)
    {
        if (scale < 0)
        {
            return v;
        }
        String temp = "#####0.";
        for (int i = 0; i < scale; i++)
        {
            temp += "0";
        }
        return Double.valueOf(new DecimalFormat(temp).format(v));
    }

    public boolean isfirstPage()
    {
        return m_isfirstPage;
    }

    public void setIslastPage(boolean islast)
    {
        m_islastPage = islast;
    }

    public boolean islastPage()
    {
        return m_islastPage;
    }

    public int getCurPostion()
    {
        return m_mbBufEnd;
    }

    public int getCurPostionBeg()
    {
        return m_mbBufBegin;
    }

    public void setBeginPos(int pos)
    {
        m_mbBufEnd = pos;
        m_mbBufBegin = pos;
    }

    public int getCurProgress()
    {
        return curProgress;
    }

    public void setFontSizePx(int size)
    {
        txtSize = size;
        txtPaint.setTextSize(size);
        int totalSize = txtSize + spaceSize;
        lineCount = (int) (mVisibleHeight / totalSize); // 可显示的行数
        rv.setTextSize(Convertor.px2sp(rv.getContext(), txtSize));
    }

    public void setBackgroundColor(int clr)
    {
        rv.setBackgroundColor(clr);
    }

    public void setFontColor(int clr)
    {
        rv.setTextColor(clr);
        btmPaint.setColor(clr);
    }

    public void setWidthAndHeight(int w, int h)
    {
        mWidth = w;
        mHeight = h;
        mVisibleWidth = mWidth - paddingLR * 2;
        mVisibleHeight = mHeight - paddingTB * 2;
    }

    public String getCommit()
    {
        if (m_lines.size() != 0)
        {
            return m_lines.get(0);
        }
        return "";
    }
}
